﻿using IronPython.Compiler.Ast;
using IronPython.Hosting;
using LunarSF.SHomeWorkshop.LunarMarkdownEditor.Utils.NPOIHelp;
using MathNet.Numerics.LinearAlgebra.Factorization;
using Microsoft.Scripting.Hosting;
using Microsoft.VisualBasic.FileIO;
using Microsoft.Windows.Shell;
using NSoup.Helper;
using Org.BouncyCastle.Tls.Crypto;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using static IronPython.Runtime.Exceptions.PythonExceptions;

namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor
{
    public class PythonScriptTreeItem : TreeViewItem, IPythonScriptItem
    {
        public PythonScriptLocation Location { get; set; } = PythonScriptLocation.Global;

        private static BitmapImage runScriptIcon;
        private static BitmapImage readOnlyOpenIcon;
        private static BitmapImage openAndEditIcon;
        private static BitmapImage locateIcon;
        private static BitmapImage deleteIcon;
        private static BitmapImage copyFromLmeScript;
        private static BitmapImage keyImage;
        private static BitmapImage folderImage;
        private static BitmapImage scriptImage;
        private static BitmapImage fileImage;
        private static BitmapImage fileWordImage;
        private static BitmapImage fileExcelImage;
        private static BitmapImage filePowerPointImage;
        private static BitmapImage fileTextImage;
        private static BitmapImage pictureImage;

        /// <summary>
        /// 此方法是静态构造方法，仅用于初始化一些用到的图标。
        /// </summary>
        static PythonScriptTreeItem()
        {
            runScriptIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/runit.png"));
            readOnlyOpenIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/readOnlyOpen.png"));
            openAndEditIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/edit.png"));
            locateIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/target.png"));
            deleteIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/delete.png"));
            copyFromLmeScript = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/copy-from-lmescript.png"));
            keyImage = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/shortcut.png"));
            folderImage = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/folder-script.png"));
            scriptImage = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/script.png"));
            fileImage = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/DocumentHS.png"));

            fileWordImage = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/word-mini.png"));
            fileExcelImage = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/excel-mini.png"));
            filePowerPointImage = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/powerpoint-mini.png"));
            fileTextImage = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/text-mini.png"));

            pictureImage = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/PictureHS.png"));
        }

        /// <summary>
        /// 构造方法一，主要用于创建目录条目以及其它一些非 Python 脚本的条目。
        /// Python 脚本条目应使用第二构造方法。
        /// </summary>
        /// <param name="fullPath"></param>
        public PythonScriptTreeItem(string fullPath)
        {
            if (File.Exists(fullPath))
            {
                fi = new FileInfo(fullPath);
                isFolder = false;
                isFile = true;

                isPythonScriptFile = fi.Name.ToLower().EndsWith(".py");

                this.entryName = fi.Name;
                this.tbTitle.Text = this.entryName;

                this.icon.Source = fileImage;
            }
            else
            {
                di = new DirectoryInfo(fullPath);
                isFolder = true;
                isFile = false;

                this.entryName = di.Name;

                if (entryName == "_Resources~")
                {
                    this.tbTitle.Text = $"资源文件夹（{this.entryName}）";
                }
                else if (entryName == "Resources")
                {
                    this.tbTitle.Text = $"资源文件夹（{this.entryName}）";
                }
                else
                {
                    this.tbTitle.Text = this.entryName;
                }

                this.icon.Source = folderImage;
            }

            this.headerWrapPanel.Children.Add(this.icon);
            this.headerWrapPanel.Children.Add(tbTitle);
            this.Header = this.headerWrapPanel;
            this.fullPath = fullPath;

            this.headerWrapPanel.PreviewMouseLeftButtonDown += HeaderWrapPanel_PreviewMouseLeftButtonDown;

            this.tbTitle.Padding = new Thickness(2, 2, 2, 2);
            this.tbTitle.Margin = new Thickness(2, 2, 2, 2);

            // 点击运行太容易误操作，暂时取消
            //tbTitle.MouseEnter += TbTitle_MouseEnter;
            //tbTitle.MouseLeave += TbTitle_MouseLeave;

            this.FontSize = 14;
            this.FontFamily = Globals.MainWindow.FontFamily;
            this.Margin = defaultMargin;

            // App.xaml 文件中未定义 MetroTreeViewItem 这个 Style
            var style = FindResource("MetroTreeViewItem") as Style;
            if (style != null) this.Style = style;
            
            this.Margin = new Thickness(0, 1, 0, 1);
            this.Background = Brushes.WhiteSmoke;

            this.popBar = new PythonScriptTreeItemPopBar(this);
            this.popBar.MouseLeave += PopBar_MouseLeave;
            this.Unselected += PythonScriptTreeItem_Unselected;

            // 在资源文件夹及其下级目录中应允许拖放一些资源文件（无论其文件类型）
            // 因为是向目录拖动而非向脚本拖动，只在这个构造方法中挂接此事件即可。
            this.AllowDrop = true;
            this.Drop += PythonScriptTreeItem_Drop;
        }

        /// <summary>
        /// 第二构造方法，仅用于创建 Python 脚本条目——涉及关联对应编辑器等特殊操作。
        /// </summary>
        /// <param name="title">脚本标题。</param>
        /// <param name="description">脚本描述文本。</param>
        /// <param name="callModeText">脚本调用方式（途径）。</param>
        /// <param name="fullPath">脚本文件完整路径。</param>
        /// <param name="shortCut">用于调用脚本的快捷键组合。</param>
        /// <param name="toolBarButtonText">脚本在工具条上显示什么文本。</param>
        /// <param name="foreground">脚本前景色。</param>
        /// <param name="background">脚本背景色。</param>
        /// <param name="groupName">脚本分组名称。</param>
        /// <param name="popupButtonGroupsText">脚本在弹出快捷工具条上的分组文本。</param>
        public PythonScriptTreeItem(string title, string description, string callModeText, string fullPath,
            string shortCut = "", string toolBarButtonText = "", Brush foreground = null, Brush background = null, string groupName = "", string popupButtonGroupsText = "")
        {
            if (File.Exists(fullPath))
            {
                this.fi = new FileInfo(fullPath);
                this.isPythonScriptFile = fi.Name.ToLower().EndsWith(".py");
            }

            this.popupButtonGroupsText = popupButtonGroupsText;

            this.Padding = new Thickness(0);

            // App.xaml 文件中未定义 MetroTreeViewItem 这个 Style
            var style = FindResource("MetroTreeViewItem") as Style;
            if (style != null) this.Style = style;

            this.Margin = new Thickness(0, 1, 0, 1);
            this.Background = Brushes.WhiteSmoke;

            StackPanel sp2 = new StackPanel()
            {
                Orientation = Orientation.Vertical,
                Margin = new Thickness(2),
            };

            tbTitle.VerticalAlignment = VerticalAlignment.Center;

            // 点击运行太容易误操作，暂时取消
            //tbTitle.MouseEnter += TbTitle_MouseEnter;
            //tbTitle.MouseLeave += TbTitle_MouseLeave;

            this.icon.Source = scriptImage;
            headerWrapPanel.Children.Add(this.icon);

            var bdBase = new Border()
            {
                Child = tbTitle,
                BorderBrush = Brushes.Transparent,
                BorderThickness = new Thickness(1, 1, 1, 1),
            };
            bdTitle.Child = bdBase;
            headerWrapPanel.Children.Add(bdTitle);
            DockPanel.SetDock(bdTitle, Dock.Left);
            if (string.IsNullOrWhiteSpace(description) == false && description != "<无描述文本>" && description != "<此处填写描述文本>")
            {
                sp2.Children.Add(tbDescription);
            }

            sp2.Children.Add(tbCallMode);
            sp2.Children.Add(tbScriptAuthor);

            if (string.IsNullOrWhiteSpace(groupName) == false && description != "<无>")
            {
                sp2.Children.Add(tbShortCut);
            }

            if (string.IsNullOrWhiteSpace(groupName) == false && description != "<无>")
            {
                sp2.Children.Add(tbGroupName);
            }

            //baseExpander.Margin =
            //    baseExpander.Padding = new Thickness();

            //baseExpander.Content = sp2;
            DockPanel.SetDock(sp2, Dock.Left);
            //DockPanel.SetDock(sp, Dock.Right);

            //this.Header = baseExpander;
            this.Header = headerWrapPanel;

            this.callMode = ConvertToPythonScriptCallMode(callModeText);

            RefreshCallMode();

            this.headerForeground = foreground;
            RefreshHeaderForeground();

            this.headerBackground = background;
            RefreshHeaderBackground();

            this.groupName = groupName;
            RefreshGroupName();

            this.title = title;  //注意这四行的顺序
            this.FullPath = fullPath;
            tbTitle.Text = this.Title;
            tbDescription.Text = description;
            this.toolBarButtonText = toolBarButtonText;

            this.shortCutText = shortCut;
            RefreshShortCut();

            MenuItem miRunScript = new MenuItem()
            {
                Style = TryFindResource("MetroMenuItem") as Style,
                Header = "执行此脚本(_R)",
                Icon = new Image()
                {
                    Source = runScriptIcon
                },
            };
            contextMenu.Items.Add(miRunScript);
            miRunScript.Click += MiRunScript_Click;

            contextMenu.Items.Add(new Separator());

            MenuItem miEditOrView = new MenuItem()
            {
                Style = TryFindResource("MetroMenuItem") as Style,
                Header = isLmePyScript ? "查看【内置】脚本(_V)" : "编辑〖自定义〗脚本(_E)",
                Icon = new Image()
                {
                    Source = isLmePyScript ? readOnlyOpenIcon : openAndEditIcon
                },
            };
            contextMenu.Items.Add(miEditOrView);
            miEditOrView.Click += MiEditOrView_Click;

            MenuItem miDeleteOrCopyAsCustomScript = new MenuItem()
            {
                Style = TryFindResource("MetroMenuItem") as Style,
                Header = isLmePyScript ? "据此创建〖自定义〗脚本(_C)" : "删除〖自定义〗脚本(_D)",
                Icon = new Image()
                {
                    Source = isLmePyScript ? copyFromLmeScript : deleteIcon
                },
            };
            contextMenu.Items.Add(miDeleteOrCopyAsCustomScript);
            miDeleteOrCopyAsCustomScript.Click += MiDeleteOrCopyAsCustomScript_Click;

            MenuItem miLocateInExplorer = new MenuItem()
            {
                Style = TryFindResource("MetroMenuItem") as Style,
                Header = "在 Explorer 中定位脚本文件(_P)",
                Icon = new Image()
                {
                    Source = locateIcon
                },
            };
            contextMenu.Items.Add(miLocateInExplorer);
            miLocateInExplorer.Click += MiPointInExplorer_Click;

            this.ContextMenu = contextMenu;
            // tbTitle.MouseLeftButtonDown += TbTitle_MouseLeftButtonDown;
            // tbTitle.MouseLeftButtonUp += TbTitle_MouseLeftButtonUp;

            this.ToolTip = sp2;
            this.Background = Brushes.WhiteSmoke;

            this.headerWrapPanel.PreviewMouseLeftButtonDown += HeaderWrapPanel_PreviewMouseLeftButtonDown;

            this.popBar = new PythonScriptTreeItemPopBar(this);
            this.popBar.MouseLeave += PopBar_MouseLeave;
            this.Unselected += PythonScriptTreeItem_Unselected;
        }

        /// <summary>
        /// 用于向资源目录（_Resources~）及其下级目录中拖放一些资源文件。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void PythonScriptTreeItem_Drop(object sender, DragEventArgs e)
        {
            var psti = sender as PythonScriptTreeItem;
            if (psti == null || psti.IsFolder == false) return;

            if (psti.Location == PythonScriptLocation.Workspace)
            {
                var relativePath = psti.RelativePath;
                if (relativePath.EndsWith("\\") == false) relativePath += "\\";

                if (relativePath.Contains("\\_Resources~\\") == false) return;
            }
            else
            {
                // 全局脚本
                string relativePath;
                if (this.fullPath.ToLower().StartsWith(Globals.PathOfUserFolder.ToLower()))
                {
                    relativePath = this.fullPath.Substring(Globals.PathOfUserFolder.Length - 1);
                    // 保留头部的 \
                }
                else relativePath = this.fullPath;

                if (relativePath.EndsWith("\\") == false) relativePath += "\\";
                if (relativePath.Contains("\\Resources\\")) return;
            }


            Array dragDataArray = ((System.Array)e.Data.GetData(DataFormats.FileDrop));
            if (dragDataArray == null || dragDataArray.Length <= 0) return;

            var filenames = new System.Collections.Specialized.StringCollection();

            foreach (string s in dragDataArray)
            {
                filenames.Add(s);
            }

            if (filenames.Count <= 0) return;

            if (filenames.Count > 100)
            {
                var answer = LMessageBox.ShowQuestion("您正打算将 100 个以上的文件拖动到此目录下。这可能耗费很长的时间，您确定要继续吗？");
                if (answer != MessageBoxResult.Yes) return;
            }

            long size = 0;

            List<FileOpration> fileOprations = new List<FileOpration>();
            foreach (var fileName in filenames)
            {
                var fi = new FileInfo(fileName);
                size += fi.Length;
                fileOprations.Add(new FileOpration()
                {
                    FromPath = fileName,
                    DestPath = Path.Combine(this.fullPath, fi.Name),
                    MasterItem = this,
                });
            }

            // 所在分区剩余空间
            long freeSpace = new DriveInfo(Path.GetPathRoot(psti.FullPath)).AvailableFreeSpace;

            if (size >= freeSpace - 1024 * filenames.Count) // 留点余地
            {
                LMessageBox.ShowWarning("您复制的文件总尺寸过大，当前分区剩余空间较小，可能导致出错。无法继续复制。");
                return;
            }

            var backTask = new 后台任务("复制资源文件");

            backTask.是否支持取消任务 = true;
            backTask.是否支持报告任务进度 = true;
            backTask.任务完成函数 += BackTask_任务完成函数;
            backTask.任务进度报告函数 += BackTask_任务进度报告函数;
            backTask.任务执行函数 += BackTask_任务执行函数;

            backTask.开始任务(fileOprations);

            e.Handled = true; // 否则上级树型节点也会执行 Drop 事件处理器。
        }

        class FileOpration
        {
            public string FromPath { get; set; }
            public string DestPath { get; set; }
            public bool IsExist { get; set; } = false;

            private PythonScriptTreeItem masterItem;

            public PythonScriptTreeItem MasterItem
            {
                get { return this.masterItem; }
                set { this.masterItem = value; }
            }
        }

        private void BackTask_任务执行函数(object sender, 任务执行事件参数 e)
        {
            var task = sender as 后台任务;
            if (task == null) return;

            var fileNames = e.参数值 as List<FileOpration>;
            if (fileNames == null || fileNames.Count <= 0)
            {
                task.取消任务();
                return;
            }

            task.置任务数(fileNames.Count);

            for (int i = 0; i < fileNames.Count; i++)
            {
                var fileName = fileNames[i];
                if (File.Exists(fileName.DestPath) == false)
                {
                    fileName.IsExist = false;
                    File.Copy(fileName.FromPath, fileName.DestPath);
                }
                else
                {
                    fileName.IsExist = true;
                }
                task.报告任务进度(i + 1, fileName);
            }
        }

        private void BackTask_任务进度报告函数(object sender, 任务进度事件参数 e)
        {
            var task = sender as 后台任务;
            if (task == null || e.状态数据 == null) return;

            var fileName = e.状态数据 as FileOpration;
            if (fileName == null) return;

            if (fileName.IsExist)
            {
                task.置任务输出信息行($"文件{fileName.DestPath}已存在！");
            }
            else
            {
                task.置任务输出信息行($"文件{fileName.DestPath}已复制！");
                fileName.MasterItem?.Items.Add(new PythonScriptTreeItem(fileName.DestPath));
            }
        }

        private void BackTask_任务完成函数(object sender, 任务完成事件参数 e)
        {
            var task = sender as 后台任务;
            if (task == null) return;

            task.置任务输出信息行("已复制拖动的资源文件！");
        }

        private void PythonScriptTreeItem_Unselected(object sender, RoutedEventArgs e)
        {
            if (this.popBar != null) this.popBar.IsOpen = false;
        }

        private void PopBar_MouseLeave(object sender, MouseEventArgs e)
        {
            if (this.popBar != null) this.popBar.IsOpen = false;
        }

        private PythonScriptTreeItemPopBar popBar;

        public PythonScriptTreeItemPopBar PopBar { get { return this.popBar; } }

        private Image icon = new Image()
        {
            Height = 14,
            Width = 14,
            VerticalAlignment = VerticalAlignment.Center,
        };

        private void HeaderWrapPanel_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount == 2)
            {
                if (this.IsPythonScriptFile)
                {
                    EditScriptFile();
                    e.Handled = true;
                }
                else if (this.IsFolder == false)  // Folder 只应双击展开/折叠，不应打开（反正已经支持定位）
                {
                    文件.打开(this.fullPath);
                    e.Handled = true;
                }
            }
            else if (e.ClickCount == 1)
            {
                this.IsSelected = true;

                if (WorkspaceTreeViewItem.IsValidateImageFilePath(this.fullPath))
                {
                    PreviewImage();
                }
                else if (WorkspaceTreeViewItem.IsValidateSoundFilePath(this.fullPath))
                {
                    PreviewSound();
                }
                else if (WorkspaceTreeViewItem.IsValidateVedioFilePath(this.fullPath))
                {
                    PreviewVedio();
                }
            }

            this.popBar.IsOpen = true; ;
        }

        public string ImageTitle
        {
            get
            {
                if (string.IsNullOrWhiteSpace(title)) return "";
                if (title.ToLower().EndsWith(".png")) return title.Substring(0, title.Length - 4);
                if (title.ToLower().EndsWith(".jpg")) return title.Substring(0, title.Length - 4);
                if (title.ToLower().EndsWith(".jpeg")) return title.Substring(0, title.Length - 5);
                if (title.ToLower().EndsWith(".gif")) return title.Substring(0, title.Length - 4);
                if (title.ToLower().EndsWith(".ico")) return title.Substring(0, title.Length - 4);
                if (title.ToLower().EndsWith(".tiff")) return title.Substring(0, title.Length - 5);
                if (title.ToLower().EndsWith(".bmp")) return title.Substring(0, title.Length - 4);

                return title;
            }
        }

        /// <summary>
        /// 预览当前项指向的图像文件。
        /// </summary>
        public void PreviewImage()
        {
            string[] files = new string[1];
            files[0] = this.fullPath;

            if (System.IO.File.Exists(this.fullPath) == false)
            {
                LMessageBox.Show("未找到此文档。可能通过其它途径（例如在Windows资源管理器中）删除了对应的磁盘文件。\r\n　　下次启动此程序时将不会再显示这个条目。",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            try
            {
                //this.masterWindow.ImagePreview.Source = new BitmapImage(new Uri(this.fullPath));//不能加此前缀"file:///" + 
                //用上面这个办法会造成图片文件无法删除，因为会被当前进程占用。
                using (BinaryReader reader = new BinaryReader(File.Open(this.fullPath, FileMode.Open)))
                {
                    FileInfo fi = new FileInfo(this.fullPath);
                    byte[] bytes = reader.ReadBytes((int)fi.Length);
                    reader.Close();

                    var bitmapImage = new BitmapImage();
                    bitmapImage.BeginInit();
                    bitmapImage.StreamSource = new MemoryStream(bytes);
                    bitmapImage.EndInit();
                    Globals.MainWindow.ImagePreview.MaxWidth = bitmapImage.PixelWidth;
                    Globals.MainWindow.ImagePreview.MaxHeight = bitmapImage.PixelHeight;
                    Globals.MainWindow.ImagePreview.Source = bitmapImage;

                    //Globals.MainWindow.ImagePreview.Tag = this.fullPath;
                    Globals.MainWindow.tbImageTitle.Text = this.ImageTitle;
                    if (string.IsNullOrWhiteSpace(Globals.MainWindow.tbImageTitle.Text))
                    {
                        Globals.MainWindow.tbImageTitle.Visibility = Visibility.Collapsed;
                    }
                    else
                    {
                        Globals.MainWindow.tbImageTitle.Visibility = Visibility.Visible;
                    }
                    bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
                }

                Globals.MainWindow.imagePreviewOutBorder.Tag = this;
                Globals.MainWindow.tcResourcePreviewArea.SelectedIndex = 0;

                //自动弹出图像预览区域并不好，容易导致用户找不到当前选中的项目。
                //if (Globals.MainWindow.rdLeftTools.ActualHeight < 40)
                //{
                //    Globals.MainWindow.rdLeftTools.Height = new GridLength(300);
                //}
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        /// <summary>
        /// 听当前项所指向的音频文件。
        /// </summary>
        private void PreviewSound()
        {
            string[] files = new string[1];
            files[0] = this.fullPath;

            if (System.IO.File.Exists(this.fullPath) == false)
            {
                LMessageBox.Show("未找到此文档。可能通过其它途径（例如在Windows资源管理器中）删除了对应的磁盘文件。\r\n　　下次启动此程序时将不会再显示这个条目。",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            var mainWin = Globals.MainWindow;

            if (mainWin.mediaElement.Statu == "Play")
            {
                mainWin.mediaElement.Pause();
            }
            else
            {
                try
                {
                    mainWin.mediaElement.Source = new Uri(this.fullPath);//不能加此前缀"file:///" + 
                    mainWin.imagePreviewOutBorder.Tag = this;
                    Globals.MainWindow.tcResourcePreviewArea.SelectedIndex = 1;
                }
                catch (Exception ex)
                {
                    LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                }
            }
        }

        /// <summary>
        /// 预览当前视频文件。
        /// </summary>
        private void PreviewVedio()
        {
            string[] files = new string[1];
            files[0] = this.fullPath;

            if (System.IO.File.Exists(this.fullPath) == false)
            {
                LMessageBox.Show("未找到此文档。可能通过其它途径（例如在Windows资源管理器中）删除了对应的磁盘文件。\r\n　　下次启动此程序时将不会再显示这个条目。",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            var mainWin = Globals.MainWindow;
            if (mainWin.mediaElement.Statu == "Play")
            {
                mainWin.mediaElement.Pause();
            }
            else
            {
                try
                {
                    mainWin.mediaElement.Source = new Uri(this.fullPath);//不能加此前缀"file:///" + 
                    mainWin.imagePreviewOutBorder.Tag = this;
                    Globals.MainWindow.tcResourcePreviewArea.SelectedIndex = 1;
                }
                catch (Exception ex)
                {
                    LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                }
            }
        }

        private Thickness defaultMargin = new Thickness(0, 3, 0, 3);

        public Thickness DefaultMargin { get => defaultMargin; }

        private PythonScriptTreeItem parentItem = null;

        public PythonScriptTreeItem ParentItem
        {
            get { return parentItem; }
            set { parentItem = value; }
        }

        /// <summary>
        /// 此方法会递归调用，故只需要根节点调用一次即可。
        /// </summary>
        public void LoadScriptEntries()
        {
            this.Items.Clear();

            if (this.IsFolder == false) return;

            var entries = this.Di.GetFileSystemInfos();
            if (entries.Length > 0)
            {
                foreach (var entry in entries)
                {
                    if (entry.FullName.EndsWith(".py", StringComparison.CurrentCultureIgnoreCase))
                    {
                        var fi = new FileInfo(entry.FullName);
                        string title = "";
                        string description = "<无描述文本>";
                        string callMode = "0";
                        string foregroundText = "Black";
                        string backgroundText = "Transparent";
                        string groupName = "";
                        string shortCutText = "";
                        string toolBarButtonText = "";
                        string popupButtonGroupsText = "";
                        Brush foreground, background;
                        Globals.MainWindow.LoadPythonScriptProperties(fi, ref title, ref description, ref callMode,
                            ref foregroundText, ref backgroundText, ref shortCutText, ref toolBarButtonText, out foreground, out background, ref groupName, ref popupButtonGroupsText);

                        if (string.IsNullOrWhiteSpace(title) || title == "未设置标题")
                        {
                            title = fi.Name;
                        }

                        if (Globals.MainWindow.ShowLmePyScripts == true || groupName != "~LME_内置示例~")
                        {
                            var psti = new PythonScriptTreeItem(title, description, callMode, fi.FullName, shortCutText, toolBarButtonText, foreground, background, groupName, popupButtonGroupsText)
                            {
                                LastWriteTime = fi.LastWriteTime,
                                Location = this.Location,
                                isPythonScriptFile = true,
                                parentItem = this,
                            };
                            this.Items.Add(psti);  // .py 文件，不可能有下级
                        }
                    }
                    else
                    {
                        var newsubtve = new PythonScriptTreeItem(entry.FullName)
                        {
                            parentItem = this,
                            Location = this.Location,
                        };

                        if (newsubtve.IsFile)
                        {
                            var exOfFileName = newsubtve.fi.Extension.ToLower();

                            if (exOfFileName == ".doc" || exOfFileName == ".docx")
                            {
                                newsubtve.icon.Source = fileWordImage;
                            }
                            else if (exOfFileName == ".xls" || exOfFileName == ".xlsx")
                            {
                                newsubtve.icon.Source = fileExcelImage;
                            }
                            else if (exOfFileName == ".ppt" || exOfFileName == ".pptx")
                            {
                                newsubtve.icon.Source = filePowerPointImage;
                            }
                            else if (exOfFileName == ".txt")
                            {
                                newsubtve.icon.Source = fileTextImage;
                            }
                            else if (exOfFileName == ".png" || exOfFileName == ".jpg" ||
                                exOfFileName == ".jpeg" || exOfFileName == ".ico" ||
                                exOfFileName == ".gif" || exOfFileName == ".bmp" || exOfFileName == ".tiff")
                            {
                                newsubtve.icon.Source = pictureImage;
                            }
                        }

                        this.Items.Add(newsubtve);   // 无论是目录还是其它非脚本文件，都是要加入的

                        if (newsubtve.IsFolder)
                        {
                            newsubtve.LoadScriptEntries();
                        }
                    }
                }
                this.IsExpanded = true;
            }
        }

        private FileInfo fi = null;

        private DirectoryInfo di = null;

        public DirectoryInfo Di { get => di; }

        private bool isFolder = false;

        public bool IsFolder { get => isFolder; }

        private bool isFile = false;

        public bool IsFile { get => isFile; }

        private string entryName;

        public string EntryName { get => entryName; }

        private bool isPythonScriptFile = false;

        public bool IsPythonScriptFile { get => isPythonScriptFile; }

        // 这种特别容易误操作
        //private void TbTitle_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        //{
        //    e.Handled = true;
        //    tbTitle.Background = Brushes.White;
        //    tbTitle.Foreground = Brushes.Black;
        //    if (tbTitlePressed)
        //    {
        //        // BtnRunScript_Click(sender, e);
        //        RunScript();
        //    }
        //    tbTitlePressed = false;
        //}

        //private void TbTitle_MouseLeave(object sender, MouseEventArgs e)
        //{
        //    if (tbTitleForeground != null)
        //    {
        //        tbTitle.Foreground = tbTitleForeground;
        //        tbTitleForeground = null;
        //    }
        //    tbTitle.Background = Brushes.Transparent;
        //    tbTitlePressed = false;
        //}

        //private Brush tbTitleForeground = null;

        //private void TbTitle_MouseEnter(object sender, MouseEventArgs e)
        //{
        //    tbTitleForeground = tbTitle.Foreground;
        //    tbTitle.Foreground = Brushes.Black;
        //    tbTitle.Background = Brushes.White;
        //    tbTitlePressed = false;
        //}

        /// <summary>
        /// 用于判断标题是否在标题内部被按下。
        /// </summary>
        // private bool tbTitlePressed = false;

        //private void TbTitle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        //{
        //    tbTitlePressed = true;
        //    tbTitle.Background = Brushes.LightGray;
        //    tbTitle.Foreground = Brushes.Black;
        //    e.Handled = true;  // 这行不能放在里面。否则 baseExpand 总会展开/折叠。
        //}

        private void MiPointInExplorer_Click(object sender, RoutedEventArgs e)
        {
            LocateInExplorer();
        }

        private void MiDeleteOrCopyAsCustomScript_Click(object sender, RoutedEventArgs e)
        {
            if (DeleteScriptFileOrCopyInnerScirpt() != null)
            {
                if (Globals.MainWindow.tcPythonScripts.SelectedItem == Globals.MainWindow.tiGlobalScriptFolder)
                {
                    var psti = Globals.MainWindow.tvGlobalScriptFolder.SelectedItem as PythonScriptTreeItem;
                    if (psti != null && psti.ParentItem != null)
                    {
                        psti.ParentItem.Items.Remove(psti);
                    }
                }
                else if (Globals.MainWindow.tcPythonScripts.SelectedItem == Globals.MainWindow.tiWorkspaceScriptFolder)
                {
                    var psti = Globals.MainWindow.tvWorkspaceScriptFolder.SelectedItem as PythonScriptTreeItem;
                    if (psti != null && psti.ParentItem != null)
                    {
                        psti.ParentItem.Items.Remove(psti);
                    }
                }
            }
            else
            {

            }
        }

        private void MiRunScript_Click(object sender, RoutedEventArgs e)
        {
            RunScript();
        }

        private void MiEditOrView_Click(object sender, RoutedEventArgs e)
        {
            EditScriptFile();
        }

        public void LocateInExplorer()
        {
            System.Diagnostics.Process.Start("explorer.exe", "/select, \"" + this.FullPath + "\"");
        }

        public PythonScriptCallMode ConvertToPythonScriptCallMode(string modeText)
        {
            switch (modeText)
            {
                case "1":
                case "１":
                    {
                        tbCallMode.Text = "调用方式：编译 Html 文件前自动调用";
                        return PythonScriptCallMode.BeforeCompileToHtml;
                    }
                case "2":
                case "２":
                    {
                        tbCallMode.Text = "调用方式：编译 Html 文件后自动调用";
                        return PythonScriptCallMode.AfterCompiledToHtml;
                    }
                case "3":
                case "３":
                    {
                        tbCallMode.Text = "调用方式：按 Ctrl + F11 调用";
                        return PythonScriptCallMode.RunByShortcuts;
                    }
                case "4":
                case "４":
                    {
                        tbCallMode.Text = "Html 工厂处理完毕前调用";
                        return PythonScriptCallMode.BeforeHtmlFactoryProcessed;
                    }
                case "5":
                case "５":
                    {
                        tbCallMode.Text = "按下 Enter 键时调用";
                        return PythonScriptCallMode.EnterPressing;
                    }
                case "6":
                case "６":
                    {
                        tbCallMode.Text = "按下 Tab 键时调用";
                        return PythonScriptCallMode.TabPressing;
                    }
                //    {
                //        tbCallMode.Text = "调用方式：随 LME 启动时调用";
                //        return PythonScriptCallMode.AutoLoadWhenStart;
                //    }
                default:
                    {
                        tbCallMode.Text = "调用方式：手动调用";
                        return PythonScriptCallMode.Default;
                    }
            }
        }

        public void RefreshCallMode(PythonScriptCallMode? mode = null)
        {
            PythonScriptCallMode cMode = callMode;

            if (mode.HasValue)
            {
                this.callMode = mode.Value;
                cMode = mode.Value;
            }

            switch (cMode)
            {
                case PythonScriptCallMode.BeforeCompileToHtml:
                    tbCallMode.Text = "调用方式：编译 Html 前自动调用";
                    tbTitle.ToolTip = null;
                    bdTitle.BorderBrush = Brushes.Transparent;
                    break;
                case PythonScriptCallMode.AfterCompiledToHtml:
                    tbCallMode.Text = "调用方式：编译 Html 后自动调用";
                    tbTitle.ToolTip = null;
                    bdTitle.BorderBrush = Brushes.Transparent;
                    break;
                case PythonScriptCallMode.RunByShortcuts:
                    tbCallMode.Text = "调用方式：按 Ctrl + F11 调用";
                    tbTitle.ToolTip = null;
                    bdTitle.BorderBrush = Brushes.Transparent;
                    break;
                case PythonScriptCallMode.BeforeHtmlFactoryProcessed:
                    tbCallMode.Text = "调用方式：Html 工厂加工前调用";
                    tbTitle.ToolTip = null;
                    bdTitle.BorderBrush = Brushes.Transparent;
                    break;
                case PythonScriptCallMode.EnterPressing:
                    tbCallMode.Text = "调用方式：按 Enter 调用";
                    tbTitle.ToolTip = null;
                    bdTitle.BorderBrush = Brushes.Transparent;
                    break;
                case PythonScriptCallMode.TabPressing:
                    tbCallMode.Text = "调用方式：按 Tab 调用";
                    tbTitle.ToolTip = null;
                    bdTitle.BorderBrush = Brushes.Transparent;
                    break;
                default:
                    tbCallMode.Text = "调用方式：手动调用";
                    tbTitle.ToolTip = null;
                    bdTitle.BorderBrush = Brushes.Transparent;
                    break;
            }

        }

        public void RefreshHeaderForeground()
        {
            this.tbTitle.Foreground = (this.headerForeground == null) ? Brushes.Black : headerForeground;
        }

        private Brush headerForeground = null;
        /// <summary>
        /// 标头文本色彩。
        /// </summary>
        public Brush HeaderForeground
        {
            get
            { return this.headerForeground; }
            set
            {
                this.headerForeground = value;
                RefreshHeaderForeground();
            }
        }

        public void RefreshHeaderBackground()
        {
            this.headerWrapPanel.Background = (this.headerBackground == null) ? Brushes.Transparent : headerBackground;
        }

        private Brush headerBackground = null;
        /// <summary>
        /// 标头文本背景色彩。
        /// </summary>
        public Brush HeaderBackground
        {
            get { return this.headerBackground; }
            set
            {
                this.headerBackground = value;
                RefreshHeaderBackground();
            }
        }

        private string groupName = "";
        /// <summary>
        /// 组名，用以将脚本分组，排序时同一组的在一起。
        /// </summary>
        public string GroupName
        {
            get { return groupName ?? ""; }
            set
            {
                this.groupName = value;
            }
        }

        public void RefreshGroupName()
        {
            if (string.IsNullOrWhiteSpace(this.groupName))
            {
                if (IsPythonScriptFile)
                {
                    this.tbTitle.ToolTip = "点击运行此脚本";
                }
                else
                {
                    this.tbTitle.ToolTip = null;
                }
                tbGroupName.Text = "分组名称：<无>";
            }
            else
            {
                if (IsPythonScriptFile)
                {
                    this.tbTitle.ToolTip = "点击运行此脚本";
                }
                else
                {
                    this.tbTitle.ToolTip = "分组名称：" + this.GroupName;
                }
                tbGroupName.Text = "分组名称：" + this.GroupName;
            }
        }

        public void RefreshShortCut()
        {
            if (string.IsNullOrWhiteSpace(this.shortCutText.ToUpper()))
            {
                tbShortCut.Text = "　快捷键：<无>";
                tbShortCut.Visibility = Visibility.Collapsed;
            }
            else
            {
                tbShortCut.Text = "　快捷键：" + this.shortCutText.ToUpper();
                TbTitle.Inlines.Add(new LineBreak());
                tbTitle.Inlines.Add(new Image()
                {
                    Height = 14,
                    Width = 14,
                    Source = keyImage,
                });
                TbTitle.Inlines.Add(new Span(new Run(shortCutText.ToUpper())));
                tbShortCut.Visibility = Visibility.Visible;
            }
        }

        private PythonScriptCallMode callMode = PythonScriptCallMode.Default;
        /// <summary>
        /// 脚本应何时调用。
        /// </summary>
        public PythonScriptCallMode CallMode { get { return this.callMode; } }

        private string shortCutText = "";
        /// <summary>
        /// 脚本的快捷键。
        /// </summary>
        public string ShortCutText
        {
            get { return this.shortCutText; }
            set { this.shortCutText = value; }
        }

        private DockPanel headerWrapPanel = new DockPanel()
        {
            HorizontalAlignment = HorizontalAlignment.Stretch,
            VerticalAlignment = VerticalAlignment.Stretch,
            LastChildFill = true,
            Margin = new Thickness(0, 0, 2, 0),
            Background = Brushes.Transparent,
        };

        private ContextMenu contextMenu = new ContextMenu();

        private TextBlock tbTitle = new TextBlock()
        {
            TextWrapping = TextWrapping.Wrap,
            HorizontalAlignment = HorizontalAlignment.Left,
            VerticalAlignment = VerticalAlignment.Center,
            Margin = new Thickness(4, 0, 0, 0),
        };

        private Border bdTitle = new Border()
        {
            BorderBrush = Brushes.Transparent,
            BorderThickness = new Thickness(1),
            SnapsToDevicePixels = true,
            VerticalAlignment = VerticalAlignment.Center,
        };

        private TextBlock tbShortCut = new TextBlock()
        {
            Margin = new Thickness(0, 6, 0, 0),
            TextWrapping = TextWrapping.Wrap,
            Foreground = Brushes.SaddleBrown,
            HorizontalAlignment = HorizontalAlignment.Left,
        };

        private TextBlock tbGroupName = new TextBlock()
        {
            Margin = new Thickness(0, 6, 0, 0),
            TextWrapping = TextWrapping.Wrap,
            Foreground = Brushes.SaddleBrown,
            HorizontalAlignment = HorizontalAlignment.Left,
        };

        public TextBlock TbTitle { get { return tbTitle; } }

        private TextBlock tbDescription = new TextBlock()
        {
            Margin = new Thickness(0, 6, 0, 0),
            TextWrapping = TextWrapping.Wrap,
            Foreground = Brushes.SaddleBrown,
            HorizontalAlignment = HorizontalAlignment.Left,
        };

        public TextBlock TbDescription { get { return tbDescription; } }

        private TextBlock tbCallMode = new TextBlock()
        {
            Margin = new Thickness(0, 6, 0, 0),
            TextWrapping = TextWrapping.Wrap,
            Foreground = Brushes.SaddleBrown,
            HorizontalAlignment = HorizontalAlignment.Left,
        };

        public TextBlock TbCallMode { get { return tbCallMode; } }

        private TextBlock tbScriptAuthor = new TextBlock()
        {
            Margin = new Thickness(0, 6, 0, 0),
            TextWrapping = TextWrapping.Wrap,
            Foreground = Brushes.SaddleBrown,
        };

        private string popupButtonGroupsText = null;
        /// <summary>
        /// 用户自定义按钮组（支持多组）。
        /// </summary>
        public string PopupButtonGroupsText
        {
            get { return popupButtonGroupsText; }
            set { popupButtonGroupsText = value; }
        }

        public TextBlock TbScriptAuthor { get { return this.tbScriptAuthor; } }

        /// <summary>
        /// 相当于按下运行按钮。
        /// </summary>
        /// <param name="groupName">使用快捷工具条按钮调用时才有必要提供值。</param>
        /// <param name="args">使用快捷工具条按钮调用时才有必要提供。</param>
        /// <returns></returns>
        internal PythonResult RunScript(string groupName = null, string args = null)
        {
            return RunScript(this.CallMode, this.FullPath, true, groupName, args);
        }

        public static string ReadString(Stream stream)
        {
            stream.Seek(0, SeekOrigin.Begin);

            // 在 VS 环境中这里应该用 Encoding.Default
            // 但一旦打包再安装，必须改成 Encoding.UTF8 才管用
            // VS 环境有些行为和实机安装的行为并不相同！
            using (var sr = new StreamReader(stream, Encoding.UTF8))
            {
                var text = sr.ReadToEnd().Replace("\r\r", "\r");
                return text;
            }
        }

        public static PythonResult RunScript(PythonScriptCallMode callMode, string scriptFullPath, bool showErrorMessage = true, string groupName = null, string args = null)
        {
            var activeEdit = Globals.MainWindow.ActivedEditor;
            if (activeEdit == null)
            {
                if (showErrorMessage)
                {
                    LMessageBox.Show("未打开任何文档！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                    return null;
                }
                else
                {
                    return new PythonResult() { ErrInfo = "未打开任何 Markdown 文件！", };
                }
            }

            string text;
            ScriptEngine engine = Python.CreateEngine();
            ScriptScope scope = engine.CreateScope();

            bool? cancel = false;
            try
            {
                if (Globals.MainWindow.rbtnSelectedText.IsChecked == true)
                {
                    // 相对于工作区根目录的路径
                    string directoryLevelMark = CustomMarkdownSupport.GetDirectoryLevelMark(activeEdit.FullFilePath);

                    text = activeEdit.EditorBase.SelectedText;

                    var sourceCode = engine.CreateScriptSourceFromFile(scriptFullPath);
                    // 运行脚本
                    scope.SetVariable("inputText", text);
                    scope.SetVariable("outputText", text);
                    scope.SetVariable("documentText", activeEdit.EditorBase.Text);
                    scope.SetVariable("ignoreTextChanging", false);
                    scope.SetVariable("pathToWorkspace", directoryLevelMark);
                    scope.SetVariable("pathOfWorkspace", Globals.PathOfWorkspace);
                    scope.SetVariable("iv_cancel", false);

                    if (string.IsNullOrWhiteSpace(groupName) == false)
                    {
                        scope.SetVariable("popButtonGroupName", groupName);
                    }

                    if (string.IsNullOrWhiteSpace(args) == false)
                    {
                        scope.SetVariable("popButtonArgs", args);
                    }

                    // 传递键盘功能键状态
                    L.GetKeyboardState(out bool isAlt, out bool isShift, out bool isCtrl);
                    scope.SetVariable("isCtrl", isCtrl);
                    scope.SetVariable("isShift", isShift);
                    scope.SetVariable("isAlt", isAlt);

                    var fi = new FileInfo(activeEdit.FullFilePath);
                    scope.SetVariable("psArgs", new PythonScriptArgs()
                    {
                        MdFileInfo = fi,
                    });
                    // 重设搜索路径，默认的不管用
                    var pathList = engine.GetSearchPaths();
                    pathList.Add(Globals.PathOfPythonScripts);
                    pathList.Add(Globals.PathOfWorkspacePythonScripts);
                    pathList.Add(Globals.InstalledPath);
                    engine.SetSearchPaths(pathList);

                    var streamOut = new MemoryStream();
                    var streamErr = new MemoryStream();
                    engine.Runtime.IO.SetOutput(streamOut, Encoding.UTF8);
                    engine.Runtime.IO.SetErrorOutput(streamErr, Encoding.UTF8);

                    scope.ImportModule("sys");
                    scope.ImportModule("clr");

                    sourceCode.Execute<string>(scope);

                    try
                    {
                        try
                        {
                            if (callMode == PythonScriptCallMode.EnterPressing || callMode == PythonScriptCallMode.TabPressing)
                            {
                                cancel = scope.GetVariable<bool>("iv_cancel");
                            }
                            else cancel = false;
                        }
                        catch
                        {
                            cancel = false;
                        }
                        // 这个要放前面

                        var ignoreTextChanging = scope.GetVariable<bool>("ignoreTextChanging");
                        var result = "";
                        if (ignoreTextChanging == false)
                        {
                            var resultText = scope.GetVariable<string>("outputText");
                            if (string.IsNullOrEmpty(resultText) == false)
                            {
                                resultText = Regex.Replace(resultText, @"((?<=[^\r])\n)|(\r(?=[^\n]))|(?<=^)\n", "\r\n");
                            }
                            result = resultText;
                            //if (result == null)
                            //    throw new ArgumentNullException("outputText");  //  现在，即使注释掉 outputText 也不要紧了——它变成内置变量了。
                            activeEdit.EditorBase.BeginChange();
                            activeEdit.EditorBase.SelectedText = result.ToString();
                            activeEdit.EditorBase.EndChange();
                        }

                        return new PythonResult()
                        {
                            OutputInfo = ReadString(streamOut),
                            ErrInfo = ReadString(streamErr),
                            InputTextValue = text,
                            OutputTextValue = result,
                            Cancel = cancel.HasValue ? cancel.Value : false,
                        };
                    }
                    catch (AccessViolationException avex)
                    {
                        if (showErrorMessage)
                        {
                            LMessageBox.Show(avex.Message);
                        }
                        else
                        {
                            return new PythonResult() { ErrInfo = avex.Message, };
                        }
                    }
                }
                else if (Globals.MainWindow.rbtnActiveDocument.IsChecked == true)
                {
                    // 相对于工作区根目录的路径
                    string directoryLevelMark = CustomMarkdownSupport.GetDirectoryLevelMark(activeEdit.FullFilePath);

                    text = activeEdit.EditorBase.Text;

                    var sourceCode = engine.CreateScriptSourceFromFile(scriptFullPath);
                    // 运行脚本
                    scope.SetVariable("inputText", text);
                    scope.SetVariable("outputText", text);
                    scope.SetVariable("documentText", text);
                    scope.SetVariable("ignoreTextChanging", false);
                    scope.SetVariable("pathToWorkspace", directoryLevelMark);
                    scope.SetVariable("pathOfWorkspace", Globals.PathOfWorkspace);
                    scope.SetVariable("iv_cancel", false);

                    // 传递键盘功能键状态
                    L.GetKeyboardState(out bool isAlt, out bool isShift, out bool isCtrl);
                    scope.SetVariable("isCtrl", isCtrl);
                    scope.SetVariable("isShift", isShift);
                    scope.SetVariable("isAlt", isAlt);

                    var fi = new FileInfo(activeEdit.FullFilePath);
                    scope.SetVariable("psArgs", new PythonScriptArgs()
                    {
                        MdFileInfo = fi,
                    });

                    var streamOut = new MemoryStream();
                    var streamErr = new MemoryStream();
                    engine.Runtime.IO.SetOutput(streamOut, Encoding.UTF8);
                    engine.Runtime.IO.SetErrorOutput(streamErr, Encoding.UTF8);

                    // 重设搜索路径，默认的不管用
                    var pathList = engine.GetSearchPaths();
                    pathList.Add(Globals.PathOfPythonScripts);
                    pathList.Add(Globals.PathOfWorkspacePythonScripts);
                    pathList.Add(Globals.InstalledPath);
                    engine.SetSearchPaths(pathList);

                    scope.ImportModule("sys");
                    scope.ImportModule("clr");

                    sourceCode.Execute<string>(scope);

                    try
                    {
                        if (callMode == PythonScriptCallMode.EnterPressing || callMode == PythonScriptCallMode.TabPressing)
                        {
                            cancel = scope.GetVariable<bool>("iv_cancel");
                        }
                        else cancel = false;
                    }
                    catch
                    {
                        cancel = false;
                    }
                    // 这个要放前面

                    var ignoreTextChanging = scope.GetVariable<bool>("ignoreTextChanging");
                    var result = "";
                    if (ignoreTextChanging == false)
                    {
                        var resultText = scope.GetVariable<string>("outputText");
                        if (string.IsNullOrEmpty(resultText) == false)
                        {
                            resultText = Regex.Replace(resultText, @"((?<=[^\r])\n)|(\r(?=[^\n]))|(?<=^)\n", "\r\n");
                        }
                        result = resultText;
                        //if (result == null)
                        //    throw new ArgumentNullException("outputText");  //  现在，即使注释掉 outputText 也不要紧了——它变成内置变量了。
                        activeEdit.EditorBase.BeginChange();
                        activeEdit.EditorBase.Document.Text = result.ToString();
                        activeEdit.EditorBase.EndChange();
                    }

                    return new PythonResult()
                    {
                        OutputInfo = ReadString(streamOut),
                        ErrInfo = ReadString(streamErr),
                        InputTextValue = text,
                        OutputTextValue = result,
                        Cancel = cancel.HasValue ? cancel.Value : false,
                    };
                }
                else if (Globals.MainWindow.rbtnOpenedDocument.IsChecked == true)
                {
                    PythonResult pResult = null;
                    foreach (var ue in Globals.MainWindow.mainTabControl.Items)
                    {
                        var edit = ue as MarkdownEditor;
                        if (edit == null) continue;

                        // 相对于工作区根目录的路径
                        string directoryLevelMark = CustomMarkdownSupport.GetDirectoryLevelMark(edit.FullFilePath);

                        var sourceCode = engine.CreateScriptSourceFromFile(scriptFullPath);
                        // 运行脚本
                        scope.SetVariable("inputText", edit.EditorBase.Text);
                        scope.SetVariable("outputText", edit.EditorBase.Text);
                        scope.SetVariable("documentText", edit.EditorBase.Text);
                        scope.SetVariable("ignoreTextChanging", false);
                        scope.SetVariable("pathToWorkspace", directoryLevelMark);
                        scope.SetVariable("pathOfWorkspace", Globals.PathOfWorkspace);
                        scope.SetVariable("iv_cancel", false);

                        // 传递键盘功能键状态
                        L.GetKeyboardState(out bool isAlt, out bool isShift, out bool isCtrl);
                        scope.SetVariable("isCtrl", isCtrl);
                        scope.SetVariable("isShift", isShift);
                        scope.SetVariable("isAlt", isAlt);

                        var fi = new FileInfo(activeEdit.FullFilePath);
                        scope.SetVariable("psArgs", new PythonScriptArgs()
                        {
                            MdFileInfo = fi,
                        });

                        var streamOut = new MemoryStream();
                        var streamErr = new MemoryStream();
                        engine.Runtime.IO.SetOutput(streamOut, Encoding.UTF8);
                        engine.Runtime.IO.SetErrorOutput(streamErr, Encoding.UTF8);

                        // 重设搜索路径，默认的不管用
                        var pathList = engine.GetSearchPaths();
                        pathList.Add(Globals.PathOfPythonScripts);
                        pathList.Add(Globals.PathOfWorkspacePythonScripts);
                        pathList.Add(Globals.InstalledPath);
                        engine.SetSearchPaths(pathList);

                        scope.ImportModule("sys");
                        scope.ImportModule("clr");

                        sourceCode.Execute<string>(scope);

                        try
                        {
                            if (callMode == PythonScriptCallMode.EnterPressing || callMode == PythonScriptCallMode.TabPressing)
                            {
                                cancel = scope.GetVariable<bool>("iv_cancel");
                            }
                            else cancel = false;
                        }
                        catch
                        {
                            cancel = false;
                        }
                        // 这个要放前面

                        var ignoreTextChanging = scope.GetVariable<bool>("ignoreTextChanging");
                        var result = "";
                        if (ignoreTextChanging == false)
                        {
                            var resultText = scope.GetVariable<string>("outputText");
                            if (string.IsNullOrEmpty(resultText) == false)
                            {
                                resultText = Regex.Replace(resultText, @"((?<=[^\r])\n)|(\r(?=[^\n]))|(?<=^)\n", "\r\n");
                            }
                            result = resultText;

                            //if (result == null)
                            //    throw new ArgumentNullException("outputText");  //  现在，即使注释掉 outputText 也不要紧了——它变成内置变量了。
                            edit.EditorBase.BeginChange();
                            edit.EditorBase.Document.Text = result.ToString();
                            edit.EditorBase.EndChange();
                        }

                        pResult = new PythonResult()
                        {
                            OutputInfo = ReadString(streamOut),
                            ErrInfo = ReadString(streamErr),
                            InputTextValue = edit.EditorBase.Text,
                            OutputTextValue = result,
                            Cancel = cancel.HasValue ? cancel.Value : false,
                        };
                    }

                    return pResult;  // 只返回最后一个，如果中途出了问题，自有 try{}catch(){} 解决。
                }
                else
                {
                    if (showErrorMessage)
                    {
                        LMessageBox.Show("请选择脚本作用范围！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                        return null;
                    }
                    else
                    {
                        return new PythonResult() { ErrInfo = "请选择脚本作用范围！", };
                    }
                }
            }
            catch (Exception ex)
            {
                ExceptionOperations eo = engine.GetService<ExceptionOperations>();
                string error = eo.FormatException(ex);
                if (showErrorMessage)
                {
                    LMessageBox.Show(error, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                    return null;
                }
                else
                {
                    // 主动结束代码（0），不显示异常消息。
                    if (string.IsNullOrWhiteSpace(error) == false && error.IndexOf("SystemExit: 0") >= 0){
                        return new PythonResult() { ErrInfo = "脚本被 sys.exit(0) 终止。", Cancel = true,};
                    }

                    var errLines = error.Replace("\r", "").Split('\n');
                    if (errLines.Length >= 4)
                    {
                        if (error.Contains("SyntaxError: invalid syntax"))
                        {
                            var reg = new Regex(@" {1,}?(?=\^)");
                            for (int i = 1; i < errLines.Length; i++)
                            {
                                var line = errLines[i];
                                var match = reg.Match(line);
                                if (match.Success)
                                {
                                    var spacesCount = match.Length;
                                    var preLine = errLines[i - 1];
                                    if (preLine.Length > spacesCount)
                                    {
                                        var preLineHeader = preLine.Substring(0, spacesCount);
                                        var newSpacesWidth = Utils.Text.GetTextWidth(preLineHeader);
                                        var syntaxErrorHeader = new string(' ', newSpacesWidth);
                                        errLines[i] = syntaxErrorHeader + "^";
                                    }
                                }
                            }
                            error = errLines.Join("\r\n");
                        }

                    }

                    return new PythonResult() { ErrInfo = error, Cancel = cancel.HasValue ? cancel.Value : false, };
                }
            }

            return null;
        }

        private bool isLmePyScript = false;
        /// <summary>
        /// [只读]是否 LME 内置的示例脚本文件。
        /// </summary>
        public bool IsLmePyScript { get { return this.isLmePyScript; } }

        public string DeleteScriptFileOrCopyInnerScirpt()
        {
            if (isLmePyScript)
            {
                try
                {
                    if (File.Exists(this.fullPath) == false)
                    {
                        LMessageBox.Show("发生意外，找不到对应的内置脚本文件了！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                        return null;
                    }

                    var srcFileInfo = new FileInfo(this.fullPath);
                    if (srcFileInfo.Name.ToLower().StartsWith("lme_") == false || srcFileInfo.Name.Length < 4)
                    {
                        LMessageBox.Show("发生意外，对应源文件名不符合内置脚本文件名命名规则！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                        return null;
                    }

                    var destFileInfo = new FileInfo(srcFileInfo.Directory + "\\" + srcFileInfo.Name.Substring(4));
                    if (destFileInfo.Exists)
                    {
                        LMessageBox.Show("已经存在同名自定义脚本！请先删除之前创建的同名脚本。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                        return null;
                    }

                    File.Copy(srcFileInfo.FullName, destFileInfo.FullName);
                    var newItem = new PythonScriptTreeItem(
                        this.title.StartsWith("【内置】") ? this.title.Substring(4) : this.title,
                        this.tbDescription.Text, tbCallMode.Text, destFileInfo.FullName)
                    {
                        Location = PythonScriptLocation.Global,  // 这种总是全局的。
                    };
                    var rootItem = Globals.MainWindow.tvGlobalScriptFolder.Items[0] as PythonScriptTreeItem;
                    if (rootItem != null)
                    {
                        rootItem.Items.Add(newItem);
                    }

                    return newItem.fullPath;
                }
                catch (Exception ex)
                {
                    LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                    return null;
                }
            }

            bool isOpen = false;
            Window editWindow = null;
            foreach (var ownedWindow in Globals.MainWindow.OwnedWindows)
            {
                var pyEdit = ownedWindow as PythonEditor;
                if (pyEdit == null) continue;
                if (pyEdit.FullPath.ToLower() == this.fullPath.ToLower())
                {
                    isOpen = true;
                    editWindow = pyEdit;
                    break;
                }
            }

            if (isOpen)
            {
                LMessageBox.Show("这个脚本正在某个脚本编辑器窗口中进行编辑，请先关闭该编辑器窗口。",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                editWindow?.Activate();
                return null;
            }

            var result = LMessageBox.Show("真的要删除此脚本文件吗？", Globals.AppName, MessageBoxButton.YesNo, MessageBoxImage.Warning);
            if (result != MessageBoxResult.Yes) return null;

            if (File.Exists(this.FullPath))
            {
                try
                {
                    // File.Delete(this.FullPath);
                    // 改成删除到回收站
                    FileSystem.DeleteFile(this.FullPath, UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin);
                    switch (this.Location)
                    {
                        case PythonScriptLocation.Workspace:
                            {
                                for (int i = Globals.MainWindow.workspacePyScriptsToolBar.Items.Count - 1; i >= 0; i--)
                                {
                                    var item = Globals.MainWindow.workspacePyScriptsToolBar.Items[i] as Button;
                                    if (item == null) continue;
                                    if (item.Tag == this)
                                    {
                                        Globals.MainWindow.workspacePyScriptsToolBar.Items.Remove(item);
                                    }
                                }

                                if (Globals.MainWindow.lbxWorkspacePythonScripts.Items.Contains(this))
                                {
                                    Globals.MainWindow.lbxWorkspacePythonScripts.Items.Remove(this);
                                }

                                // 新全局脚本管理器中删除
                                if (this.ParentItem != null && this.ParentItem.Items.Contains(this))
                                {
                                    this.ParentItem.Items.Remove(this);
                                }
                                break;
                            }
                        // PythonScriptLocation.Global:
                        default:
                            {
                                for (int i = Globals.MainWindow.pyScriptsToolBar.Items.Count - 1; i >= 0; i--)
                                {
                                    var item = Globals.MainWindow.pyScriptsToolBar.Items[i] as Button;
                                    if (item == null) continue;
                                    if (item.Tag == this)
                                    {
                                        Globals.MainWindow.pyScriptsToolBar.Items.Remove(item);
                                    }
                                }

                                if (Globals.MainWindow.lbxPythonScripts.Items.Contains(this))
                                {
                                    Globals.MainWindow.lbxPythonScripts.Items.Remove(this);
                                }

                                // 新工作区脚本管理器中删除
                                if (this.ParentItem != null && this.ParentItem.Items.Contains(this))
                                {
                                    this.ParentItem.Items.Remove(this);
                                }
                                break;
                            }
                    }
                    return "";
                }
                catch (Exception ex)
                {
                    LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                    return null;
                }
            }
            else return null;
        }

        public void EditScriptFile()
        {
            try
            {
                if (File.Exists(this.FullPath) == false)
                {
                    LMessageBox.Show($"Python 脚本文件【{this.FullPath}】不存在！", Globals.AppName, System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Warning);
                    return;
                }

                var info = new FileInfo(this.FullPath);
                if (info.Extension.ToLower() != ".py")
                {
                    LMessageBox.Show($"Python 脚本文件【{info.Name}】不存在！", Globals.AppName, System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Warning);
                    return;
                }

                if (this.Editor != null)
                {
                    LMessageBox.Show($"脚本【{this.Title}】的编辑器窗口已经被打开。请勿重复打开编辑器窗口！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);
                    this.Editor.Activate();
                    return;
                }
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning("发生意外，无法打开已初始化的编辑器窗口！异常消息如下：\r\n　　" + ex.Message);
                return;
            }

            var editor = new PythonEditor(this.FullPath, tbTitle.Text, this)
            {
                Owner = Globals.MainWindow,
                MasterItem = this,
            };
            this.Editor = editor;

            var scaleX = 96.00 / Utils.ScreenDPIHelper.DpiX;
            var scaleY = 96.00 / Utils.ScreenDPIHelper.DpiY;

            var screenWidth = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Width * scaleX;
            editor.Width = screenWidth / 3;
            editor.Left = editor.Width * 2;
            editor.Height = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Height * scaleY;
            editor.Top = 0;

            if (Globals.MainWindow.WindowScriptEditiongMode == 2)
            {
                Globals.MainWindow.Width = editor.Left;
                Globals.MainWindow.Height = editor.Height;
                Globals.MainWindow.Left = 0;
                Globals.MainWindow.Top = 0;
                Globals.MainWindow.WindowState = WindowState.Normal;
            }

            editor.Show();
        }

        public PythonEditor Editor { get; set; } = null;

        private string fullPath = "";
        /// <summary>
        /// [读写]对应的 Python 脚本文件的完整磁盘路径。
        /// </summary>
        public string FullPath
        {
            get { return this.fullPath; }
            set
            {
                this.fullPath = value;
                RefreshFullPath();
            }
        }

        private string title = "";

        public string Title
        {
            get { return this.title; }
            set
            {
                this.title = value;
                this.tbTitle.Text = value;
            }
        }

        private string toolBarButtonText = "";

        public string ToolBarButtonText
        {
            get { return this.toolBarButtonText; }
            set { this.toolBarButtonText = value; }
        }

        private void RefreshFullPath()
        {
            if (File.Exists(fullPath) == false)
            {
                isLmePyScript = false;
                if (this.title.StartsWith("【内置】"))
                    this.title = this.title.Substring(4);
            }
            else
            {
                var fi = new FileInfo(this.fullPath);
                isLmePyScript = fi.Name.ToLower().StartsWith("lme_");
                if (isLmePyScript)
                {
                    if (this.title.StartsWith("【内置】") == false)
                        this.title = "【内置】" + this.title;
                    tbScriptAuthor.Text = "脚本来源：LME 内置的示例脚本";
                }
                else
                {
                    if (this.title.StartsWith("【内置】"))
                        this.title = this.title.Substring(4);
                    tbScriptAuthor.Text = "脚本来源：用户编写的自定义脚本";
                }
            }
        }

        /// <summary>
        /// 最后修改时间。
        /// </summary>
        public DateTime LastWriteTime { get; set; }

        /// <summary>
        /// 返回相对于工作区的路径（以 \ 开头）。
        /// </summary>
        public string RelativePath
        {
            get
            {
                if (string.IsNullOrWhiteSpace(this.fullPath)) return null;

                if (this.fullPath.ToLower().StartsWith(Globals.PathOfWorkspace.ToLower()))
                {
                    return this.fullPath.Substring(Globals.PathOfWorkspace.Length - 1);
                }

                return this.fullPath;
            }
        }

        internal bool RunScriptByShortCut(bool isShift, bool isAlt, bool isCtrl, Key key)
        {
            if (string.IsNullOrWhiteSpace(ShortCutText)) return false;

            var spans = ShortCutText.Split(new char[] { '+', }, StringSplitOptions.RemoveEmptyEntries);
            if (spans.Length < 1) return false;

            string keyText = spans[spans.Length - 1].Trim(new char[] { ' ', '　', '\t' });
            if (string.IsNullOrWhiteSpace(keyText)) return false;

            bool hasShift = false;
            bool hasCtrl = false;
            bool hasAlt = false;

            foreach (var s in spans)
            {
                var span = s.ToLower();
                if (span.Contains("shift")) hasShift = true;
                if (span.Contains("alt")) hasAlt = true;
                if (span.Contains("ctrl")) hasCtrl = true;
            }

            if (hasShift != isShift) return false;
            if (hasCtrl != isCtrl) return false;
            if (hasAlt != isAlt) return false;

            if (keyText.ToLower() != key.ToString().ToLower()) return false;

            var result = RunScript(this.CallMode, this.FullPath);
            return string.IsNullOrEmpty(result.ErrInfo);
        }
    }
}
